﻿#include "netTool.h"
#include "unistd.h"

#include <vector>
#ifdef _WIN32
#include <WinSock2.h>
#else // WIN32
#include <sys/types.h>
#include <sys/socket.h>
#endif

// fd的添加与删除是否应该独立出来，每次select时进行同步
int SelectHelper::FD_SELECT(int fd, RECV_CB rCb)
{	
	std::lock_guard<std::mutex> lock(mutex_table_fd);
	if (fd > 0 && table_fd.find(fd) == table_fd.end())
	{
		table_fd.insert(std::make_pair(fd, rCb));
		return 0;
	}
	return -1;
}

int SelectHelper::FD_CANCEL(int fd)
{
	std::lock_guard<std::mutex> lock(mutex_table_fd);
	table_fd.erase(fd);
	return 0;
}

int SelectHelper::thrSelect()
{
	fd_set fdSet;
	int MAX_FD = 0;
	std::map<int, RECV_CB> copyTableFd;
	
	{
		selectFdSync(copyTableFd);


		if (copyTableFd.empty())
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
			return 0;
		}

		FD_ZERO(&fdSet);
		for (auto val : copyTableFd)
		{
			FD_SET(val.first, &fdSet);
		}
		MAX_FD = copyTableFd.rbegin()->first;

	}

	//
	struct timeval ts = { 1, 0 };
	int ret = select(MAX_FD + 1, &fdSet, nullptr, nullptr, &ts);
	if (ret == 0)
	{
		return 0;
	}
	else if (ret > 0)
	{
		
		
		for (auto val : copyTableFd)
		{
			if (FD_ISSET(val.first, &fdSet))
			{
				char recvBuf[SERIAL_BUFFER_SIZE] = {};

				ret = recv(val.first, recvBuf, SERIAL_BUFFER_SIZE, 0);

				// 如果fd出现异常会通过回调函数中的ret值体现，使用者应主动取消监听并关闭fd
				val.second(val.first, recvBuf, ret);
			}
		}
	}
	else
	{
		lcout << "error select " << ret << std::endl;
		return -1;
	}
	return 0;
}


void SelectHelper::selectFdSync(std::map<int, RECV_CB>& copy)
{
	{
		std::lock_guard<std::mutex> lock(mutex_table_fd);
		copy = table_fd;
	}
}


// --------------------------------------------	version 2 ----------------------------------

int SelectHelperV2::FD_SELECT(int fd, cmdWorker *parse)
{
    std::lock_guard<std::mutex> lock(mutex_table_fd);
	if (parse && fd > 0 && table_fd.find(fd) == table_fd.end())
	{
		if(thrHelper == nullptr){
			thrHelper.reset(new ThreadHelper(std::bind(&SelectHelperV2::thrSelect,this)));
		}
		table_fd.insert(std::make_pair(fd, parse));
		return 0;
	}
	return -1;
}

int SelectHelperV2::FD_CANCEL(int fd)
{
    std::lock_guard<std::mutex> lock(mutex_table_fd);
	table_fd.erase(fd);
	return 0;
}

int SelectHelperV2::thrSelect()
{
    fd_set fdSet;
	int MAX_FD = 0;
	std::map<int, cmdWorker*> copyTableFd;
	
	{
		selectFdSync(copyTableFd);


		if (copyTableFd.empty())
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
			return 0;
		}

		FD_ZERO(&fdSet);
		for (auto val : copyTableFd)
		{
			FD_SET(val.first, &fdSet);
		}
		MAX_FD = copyTableFd.rbegin()->first;
	}

	//
	struct timeval ts = { 1, 0 };
	int ret = select(MAX_FD + 1, &fdSet, nullptr, nullptr, &ts);
	if (ret == 0)
	{
		return 0;
	}
	else if (ret > 0)
	{
		
		std::vector<int> errFd;
		for (auto& val : copyTableFd)
		{
			if (FD_ISSET(val.first, &fdSet))
			{
				char recvBuf[SERIAL_BUFFER_SIZE] = {};

				//ret = recv(val.first, recvBuf, SERIAL_BUFFER_SIZE, 0);
				ret = read(val.first, recvBuf, SERIAL_BUFFER_SIZE);
				if (ret > 0)
				{
					std::string repStr;
					cmdWorker *parse = val.second;
					parse->protocalParse(val.first,recvBuf, ret, repStr);
					if (repStr.empty() == false)
					{
						ret = send(val.first, repStr.data(), repStr.size(), 0);
						lcout << "send data size " << ret;
					}
				}else{
					errFd.push_back(val.first);
				}
			}
		}

		
		if(errFd.empty() == false){
			// 检测是否是已取消检测的fd返回的错误
			std::lock_guard<std::mutex> lock(mutex_table_fd);
			for(auto& valFD: errFd){
				auto iter = table_fd.find(valFD);
				if(iter != table_fd.end()){		// 通知持有者fd异常
					std::string strNUll;
					iter->second->protocalParse(valFD,NULL,-1,strNUll);
				}
			}
		}
	}
	else
	{
		lcout << "error select " << ret << std::endl;
		return -1;
	}
	return 0;
}

void SelectHelperV2::selectFdSync(std::map<int, cmdWorker *> &copy)
{
	std::lock_guard<std::mutex> lock(mutex_table_fd);
	copy = table_fd;
	
	return;
}
